﻿/*
 * candevice.cpp
 *
 *  Created on: 2017年3月21日
 *      Author: work
 */

#include <dm/os/com/candevice.hpp>
#include <dm/os/log/logger.hpp>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

#include <cstring>

namespace dm{
namespace os{
namespace com{

using namespace boost::asio;

static const char* logModule = "CCanDevice.com.os.dm";

#ifndef PF_CAN
#define PF_CAN 29
#endif

CCanDevice::CCanDevice( ios_t& ios,const size_t& rxBufSize ):CDevice(rxBufSize),
		m_dev(ios){
	log().debug( THISMODULE "创建对象");
}

CCanDevice::~CCanDevice(){
	m_dev.shutdown( m_dev.shutdown_both );
	m_dev.close();
	log().debug( THISMODULE "销毁对象");
}

bool CCanDevice::checkAndSetAddress( const CCanAddr& addr ){
	m_addr = addr;
	return true;
}

class CCanNetIoControl{
public:
	CCanNetIoControl( const char* canDev ){
		std::strcpy(m_d.ifr_name,canDev);
	};

	int name(){
		return SIOCGIFINDEX;
	}
	void* data(){
		return &m_d;
	}

public:
	struct ifreq m_d;
};

class CCanNetFilter{
public:
	template <typename Protocol>
	int level(const Protocol&) const{
		return SOL_CAN_RAW;
	}

	template <typename Protocol>
	int name(const Protocol&) const{
		return CAN_RAW_FILTER;
	}

	template <typename Protocol>
	const void* data(const Protocol&) const{
		return &filter;
	}

	template <typename Protocol>
	std::size_t size(const Protocol&) const{
	    return sizeof(filter);
	}
public:
	struct can_filter filter;
};

bool CCanDevice::startConnect(){
	log().debug( THISMODULE "打开设备%s",m_addr.toString().c_str());
	try{
		const CCanAddr* addr = m_addr.asCan();
		if( !addr )
			return false;

		m_dev.close();

		// 关闭设备，设置bps，启动设备
		char buf[128];
		sprintf(buf,"ifconfig %s down;ip link set %s type can bitrate %d;ifconfig %s up",
				addr->getDevice().c_str(),
				addr->getDevice().c_str(),addr->getBps(),
				addr->getDevice().c_str());

		pid_t p = fork();
		if( p<0 ){
			log().error(THISMODULE "创建进程失败");
			return false;
		}else if( p==0 ){
			system(buf);
			log().debug(THISMODULE "执行命令:%s",buf);
			exit(0);
		}else{
			// 回收子进程
			int status;
			wait(&status);
		}

		m_dev.assign( boost::asio::generic::raw_protocol(PF_CAN,SOCK_RAW),socket(PF_CAN, SOCK_RAW, CAN_RAW));

		CCanNetIoControl ioCtl( addr->getDevice().c_str());

		m_dev.io_control<CCanNetIoControl>(ioCtl);

		struct sockaddr_can ca;
		ca.can_family = PF_CAN;
		ca.can_ifindex = ioCtl.m_d.ifr_ifindex;

		m_dev.bind( boost::asio::generic::raw_protocol::endpoint((struct sockaddr *)&ca, sizeof(ca)));

		if( addr->getId()!=0 ){
//			struct can_filter rfilter;
//			rfilter.can_id   = addr->getId();
//			rfilter.can_mask = addr->getMask();

			CCanNetFilter filter;
			filter.filter.can_id = addr->getId();
			filter.filter.can_mask = addr->getMask();

			m_dev.set_option(filter);
		}


		handler_connect();
	}catch( std::exception& ec ){
		log().warnning( THISMODULE "open %s fail(%s)",m_addr.toString().c_str(),ec.what());
		handler_connect(boost::system::errc::make_error_code(boost::system::errc::bad_address));
		return false;
	}

	return true;
}

bool CCanDevice::stopConnect(){
	m_dev.close();
	return true;
}

bool CCanDevice::startSend( const dm::uint8* buf,const size_t& len ){
	try{
		m_dev.async_send( buffer(buf,len),boost::bind(&CCanDevice::handler_tx,this,boost::asio::placeholders::error));
	}catch( std::exception& ec ){
		log().warnning(THISMODULE "send %d bytes fail(%s)",len,ec.what());
		return false;
	}
	return true;
}

void CCanDevice::start_rx(){
	try{
		m_dev.async_receive(buffer(m_rxBuf.get(),m_rxBufSize),boost::bind(&CCanDevice::handler_rx,this,placeholders::error,placeholders::bytes_transferred));
	}catch( std::exception& ec ){
		log().error(THISMODULE "开启监听失败%s",ec.what());
	}
}

void CCanDevice::cancelDev(){
	try{
		m_dev.close();
	}catch( std::exception& ec ){
		log().error(THISMODULE "取消设备失败%s",ec.what());
	}

}
}
}
}


